package kioscodeal

import org.springframework.dao.DataIntegrityViolationException

import static org.springframework.http.HttpStatus.*
import grails.transaction.Transactional

@Transactional(readOnly = true)
class PedidoController {

    static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
	
	def beforeInterceptor = [action:this.&auth,
		except:["geolocalize", "compra", "cargar","create","save","aplicarDescuentos","saveCarrito","edit", 
			"eliminarElementoCarrito", "vaciarCarrito", "indexPorUsuario"]]
	
	def geocodingService
	
	def scaffold = Pedido
	
	private static final double FACTOR = 0.06 * 1.1515 * 1.609344 * 1000 * 1000
	
	
	def auth() {
		if(!(session?.user?.role == Role.ADMINISTRATOR)){
			flash.message = "Usted debe ser Administrador para realizar esta tarea."
			redirect(uri:'/')
			return false
		}
	}

    def index(Integer max) {
        params.max = Math.min(max ?: 10, 100)
        respond Pedido.list(params), model:[pedidoInstanceCount: Pedido.count()]
    }
	
	@Transactional
	def geolocalize(Pedido pedidoInstance){
		def coordenada = new Coordenada()
		coordenada.save()
		pedidoInstance.coord = coordenada
		
		def latlng = geocodingService.findLatLngByAddress("${pedidoInstance?.cliente?.calle} ${pedidoInstance?.cliente?.numeroDireccion}, ${pedidoInstance?.cliente?.localidad}")
		if(latlng){
			pedidoInstance.coord.lat = latlng.lat
			pedidoInstance.coord.lng = latlng.lng
		}
		
		def comercioInstance = Comercio.findById(1)
		def coord1 = comercioInstance.coord
		def coord2 = pedidoInstance.coord
		
		pedidoInstance.distanciaComercioCliente = Math.toDegrees(Math.acos(Math.sin(Math.toRadians(coord1['lat'])) * Math.sin(Math.toRadians(coord2['lat'])) +
		Math.cos(Math.toRadians(coord1['lat'])) * Math.cos(Math.toRadians(coord2['lat'])) *
		Math.cos(Math.toRadians(coord1['lng'] - coord2['lng'])))) * FACTOR
		
		if (!pedidoInstance.save(flush: true)) {
            render(view: "create", model: [pedidoInstance: pedidoInstance])
            return
		}
		flash.message = message(code: 'default.created.message', args: [message(code: 'pedido.label', default: 'Pedido'), pedidoInstance.id])
		redirect(action: "show", id: pedidoInstance.id)
	}

	def show(Pedido pedidoInstance) {
        respond pedidoInstance
    }

    def create() {
        respond new Pedido(params)
    }

    @Transactional
    def save(Pedido pedidoInstance) {
        if (pedidoInstance == null) {
            notFound()
            return
        }

        if (pedidoInstance.hasErrors()) {
            respond pedidoInstance.errors, view:'create'
            return
        }

        pedidoInstance.save flush:true

        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.created.message', args: [message(code: 'pedido.label', default: 'Pedido'), pedidoInstance.id])
                redirect pedidoInstance
            }
            '*' { respond pedidoInstance, [status: CREATED] }
        }
    }

    def edit(Pedido pedidoInstance) {
        respond pedidoInstance
    }

    @Transactional
    def update(Pedido pedidoInstance) {
        if (pedidoInstance == null) {
            notFound()
            return
        }

        if (pedidoInstance.hasErrors()) {
            respond pedidoInstance.errors, view:'edit'
            return
        }

        pedidoInstance.save flush:true

        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.updated.message', args: [message(code: 'Pedido.label', default: 'Pedido'), pedidoInstance.id])
                redirect pedidoInstance
            }
            '*'{ respond pedidoInstance, [status: OK] }
        }
    }

    @Transactional
    def delete(Pedido pedidoInstance) {

        if (pedidoInstance == null) {
            notFound()
            return
        }

        pedidoInstance.delete flush:true

        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.deleted.message', args: [message(code: 'Pedido.label', default: 'Pedido'), pedidoInstance.id])
                redirect action:"index", method:"GET"
            }
            '*'{ render status: NO_CONTENT }
        }
    }

    protected void notFound() {
        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.not.found.message', args: [message(code: 'pedido.label', default: 'Pedido'), params.id])
                redirect action: "index", method: "GET"
            }
            '*'{ render status: NOT_FOUND }
        }
    }
	
	/**************************************************  COMPRA  ********************************************************************/
	def compra(Integer max) {
		session.carrito = null
		session.noSuperaImporteMinimo = false
		params.max = Math.min(max ?: 10, 100)
		
		def listado = Producto.list(params)
		def productoPedidoInstanceList = new ArrayList<ProductoPedido>();
		
		for (def producto in Producto.list(params)){
			ProductoPedido productoPedido = new ProductoPedido()
			productoPedido.producto = producto
			productoPedido.cantidad = 0;
			productoPedidoInstanceList.add(productoPedido)
		}
		
		def listadoCarrito = new ArrayList<ProductoPedido>()
		def listadoCompra = new ArrayList<ProductoPedido>()

		respond productoPedidoInstanceList, model:[listadoCarrito: listadoCarrito, listadoCompra:listadoCompra, 
			importeTotalCompra:0.00, porcentajeDescuento:0.00]
	}
	
	def cargar(Long idProducto, Integer cantidadCarrito) {
		if (session.carrito == null){
			if (cantidadCarrito > 0){
				session.carrito = [
					[producto: idProducto, cantidad: cantidadCarrito]
				]
			}
		} else {
			boolean encontrado = false
			session.carrito.each{
				if (it.'producto' == idProducto){
					it.'cantidad' = cantidadCarrito
					encontrado = true					
				}
			}
			if (!encontrado && cantidadCarrito > 0){
				session.carrito += [
					[producto: idProducto, cantidad: cantidadCarrito]
				]
			} else if (encontrado && cantidadCarrito <= 0){
				buscarYeliminarElementoCarrito(idProducto)
			}
		}
		
		def productoPedidoInstanceList = new ArrayList<ProductoPedido>()
		
		for (def producto in Producto.findAll()){
			ProductoPedido productoPedido = new ProductoPedido()
			productoPedido.producto = producto
			productoPedido.cantidad = 0;
			session.carrito.each{
				if (it.'producto' == producto.id){
					productoPedido.cantidad = it.'cantidad'					
				}
			}
			productoPedido.importeTotal = producto.precioUnitario * productoPedido.cantidad
			productoPedidoInstanceList.add(productoPedido)
		}
		
		def importeCompra = 0.00
		def listadoCompra = new ArrayList<ProductoPedido>();
		for (def pp in productoPedidoInstanceList){
			if (pp.cantidad > 0){
				listadoCompra.add(pp)
				importeCompra += pp.importeTotal
			}
		}
		
		def listadoCarrito = new ArrayList<ProductoPedido>()
		
		//Descuento
		def descuentoAplicar = aplicarDescuentos(importeCompra)
		def porcentajeDescuento = 0.00
		//Aplico descuento
		if (descuentoAplicar != null){
			importeCompra -= (importeCompra * (descuentoAplicar.porcentaje / 100))
			porcentajeDescuento = descuentoAplicar.porcentaje
		}
		
		render(view: "compra", model:[productoPedidoInstanceList: productoPedidoInstanceList, listadoCarrito: listadoCarrito, listadoCompra: listadoCompra,
			importeTotalCompra:importeCompra, porcentajeDescuento: porcentajeDescuento])
	}
	
	def aplicarDescuentos(Double importeCompra){
		def descuentoAplicar = null
		def descuentos = Descuento.findAll()
		descuentos.each { Descuento unDescuento ->
			if (unDescuento.importeAplicado <= importeCompra){
				def fechaActual = new Date()
				if (unDescuento.fechaDesde.getTime() <= fechaActual.getTime() && unDescuento.fechaHasta.getTime() >= fechaActual.getTime()){
					if (descuentoAplicar != null && descuentoAplicar.porcentaje < unDescuento.porcentaje){
						descuentoAplicar = unDescuento
					} else {
						descuentoAplicar = unDescuento
					}
				}
			}
		}
		
		return descuentoAplicar
	}
	
	@Transactional
	def saveCarrito() {
		if (session.carrito != null){
			def importeCompra = 0.00
			def listadoPedido = null;
			session.carrito.each{
				if (it.'cantidad' > 0){
					def productoCarrito = Producto.findById(it.'producto')
					if (productoCarrito != null){
						def productoPedido = new ProductoPedido(
								producto: productoCarrito,
								cantidad: it.'cantidad',
								estadoProductoPedido: EstadoProductoPedido.NO_ENVIADO
							).save(failOnError:true)
						
						if (listadoPedido == null && productoPedido != null){
							listadoPedido = [productoPedido]
							importeCompra += (productoPedido.cantidad * productoPedido.producto.precioUnitario)
						} else if (listadoPedido != null && productoPedido != null){
							listadoPedido += [productoPedido]
							importeCompra += (productoPedido.cantidad * productoPedido.producto.precioUnitario)
						}
					}
				}
			}
			
			def comercioInstance = Comercio.findById(1)
						
			def importeOk = (session?.user.minimoCompra == null || session?.user.minimoCompra == 0) ? comercioInstance.minimoCompraGeneral : session?.user.minimoCompra
			if (importeCompra >= importeOk){
				procesarCarrito(listadoPedido, importeCompra)
			} else {
				session.noSuperaImporteMinimo = true
				
				def importeFaltante = 0.00				
				if (session?.user.minimoCompra != null && importeCompra < session?.user.minimoCompra){
					importeFaltante = session?.user.minimoCompra - importeCompra
				} else {
					importeFaltante = comercioInstance.minimoCompraGeneral - importeCompra
				}
				
				session.importeMinimoFaltante = importeFaltante
				cargar(-1, 0)
			}			
		}					
	}
	
	def procesarCarrito(def listadoPedido, def importeCompra){
		def pedidoInstance = null
		if (listadoPedido != null){
			//Limpio los datos en sesion
			session.carrito = null
			
			//Descuento
			def descuentoAplicar = aplicarDescuentos(importeCompra)
			//Aplico descuento
			if (descuentoAplicar != null){
				importeCompra -= (importeCompra * (descuentoAplicar.porcentaje / 100))
			}
			
			pedidoInstance = new Pedido(
				cliente: session?.user,
				estadoPedido: EstadoPedido.PENDIENTE,
				calificacionPedido: CalificacionPedido.SIN_CALIFICACION,
				fechaCreacion: new Date(),
				productoPedidos: listadoPedido,
				descuento: descuentoAplicar,
				importeTotal: importeCompra
			)
		}
		
		if (pedidoInstance != null){
			geolocalize(pedidoInstance)
		} else {
			cargar(-1, 0)
		}
	}
	
	def eliminarElementoCarrito(Producto producto){
		buscarYeliminarElementoCarrito(producto.id)
		cargar(-1, 0)
	}
	
	def buscarYeliminarElementoCarrito(Long idProducto){
		if (session.carrito != null){
			def cantidadProducto = 0
			def encontrado = true
			session.carrito.each{
				if (it.'producto' == idProducto){
					encontrado = true
					cantidadProducto = it.'cantidad'
				}
			}
			
			if (encontrado){
				session.carrito.remove([producto: idProducto, cantidad: cantidadProducto])
			}
		}
	}
	
	def vaciarCarrito(){
		session.carrito = null
		cargar(-1, 0)
	}
	
	
	/**************************************************  Listado Pedido por Usuario  ********************************************************************/
	def indexPorUsuario() {		
		def listadoOk = new ArrayList<Pedido>()
		
		Pedido.findAll().each{
			if (it.cliente.id == session?.user.id){
				listadoOk.add(it)
			}
		}
		
		render(view: "index", model:[pedidoInstanceList: listadoOk, pedidoInstanceCount: listadoOk.size()])
	}
}
